home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / Debugging OpenDoc / Tools / Refbal / refbal.readme < prev    next >
Encoding:
Text File  |  1995-07-11  |  21.1 KB  |  517 lines  |  [TEXT/MPS ]

  1. refbal.readme (version: Monday, July 10, 1995 1:15 PM)
  2. David McCusker
  3.    
  4. === Overview =====================================================
  5.  
  6. Refbal is an MPW tool which looks for ref-counting problems in C or C++
  7. sources for OpenDoc.  It scans source files heuristically looking for
  8. patterns that look like problems and prints out error messages.  The
  9. primary situation it understands is unbalanced acquires and releases inside
  10. an individual function.  Refbal's main goal is to require balanced acquires
  11. and releases inside a function for every variable that is assigned an
  12. acquired value.  If you acquire a value, refbal wants you to assign it
  13. to a variable, and then it wants you to release it. :-)  In addition to this 
  14. main goal, refbal also attempts to impose the requirement that acquired
  15. values only be returned from functions with "Acquire" in their names.  To
  16. this end, returning a required value is generally an appropriate substitute 
  17. for releasing it.
  18.  
  19. Refbal is also concerned that you delete instances of objects, like
  20. ODStorageUnitView, which are not ref-counted themselves, but contain
  21. references to ref-counted objects they have acquired.
  22.  
  23. Refbal is marginally useful to help find cross-function problems with
  24. ref-counting, but only by calling attention to those places where acquired
  25. values are not balanced in individual functions (because you are going
  26. to balance them across functions, right?).  (Perhaps you should draw a
  27. data flow diagram to illustrate cross-function balances, with acquires
  28. as the source of an arrow, and requires as the sink.) 
  29.  
  30. The most common way in which acquired values are passed between functions
  31. is through assignment to member variables.  Therefore, if you are not
  32. interested in seeing cross-function imbalances caused by member variables,
  33. the most effective way of shutting out this noise is to suppress analysis
  34. of variables which look like member variables.  In the OpenDoc sources,
  35. member variable references typically begin with either /_/ or /f[A-Z]/,
  36. so refbal ignores variable names like this by default.  If you want refbal
  37. to help you with balancing your member variable references, the most you
  38. can do is enable the error reports for these variables to help call your
  39. attention to these strategic points in your code.  [Better support for
  40. developer's member variable names could have been provided by using
  41. regular expressions to describe them; but this seemed more complicated
  42. than warranted.]
  43.  
  44. Sometimes refbal appears to be pretty stupid about C and C++ syntax, and
  45. this is because it *is* stupid about C and C++.  It makes all judgments
  46. based on patterns that occur in just a few tokens of interest--mainly
  47. identifiers, braces, parens, semicolons, and the assignment operator.
  48.  
  49. === Limitations ====================================================
  50. Refbal cannot find ref-counting problems that are not apparent from
  51. static analysis of individual functions.  The most obvious case involves
  52. objects that are acquired in one function, but released in another.
  53.  
  54. A less obvious ref-counting problem involves objects which are *not*
  55. ref-counted, but which refer to objects which *are* ref-counted.  You
  56. must be certain to delete any such objects in your code, so they have
  57. an opportunity to release their ref-counted objects.
  58.  
  59. ODFacet is an example of a non-ref-counted object which has references 
  60. to ref-counted objects like ODFrame, ODShape, and ODCanvas.  However, the 
  61. OpenDoc API never gives a developer a pointer to a facet which should be
  62. directly deleted.  Instead, a developer must be sure to use the OpenDoc API
  63. to remove all facets from an embedded frame, or otherwise the frame ref-count
  64. will not reach zero due to outstanding references that were not released.
  65.  
  66. === Warnings You Can Ignore ========================================
  67. Suppose you have a member variable named _fContFrame which points to an
  68. ODFrame, and your code contains a code fragment like the following:
  69.  
  70.     ODFrame* containingFrame = frame->AcquireContainingFrame(ev);
  71.     _fContFrame = containingFrame;
  72.     
  73. Presumably _fContFrame assumes ownership of the reference that was
  74. acquired for containingFrame, and some other function will release 
  75. the value that was assigned to _fContFrame.  Refbal will complain
  76. that containingFrame was not released; but when you look at your
  77. code, you will see that containingFrame is subsequently assigned to
  78. a member variable.  You should expect a number of such warnings in
  79. your code; they are safe to ignore provided you correctly release
  80. your member variables later.
  81.  
  82. Similarly, refbal will probably complain about ref-counted objects
  83. which you acquire and put into a collection (like a list of
  84. frames), or remove from a collection and then release, because the
  85. acquires and releases do not occur all in a single function.
  86.  
  87. Refbal also generates some unnecessary warnings due to simple inability
  88. to evaluate which sections of code are executed in conditional
  89. expressions.  For example you might re-assign to a variable when it is
  90. nil, but to refbal this looks like you are losing a ref-count reference:
  91.  
  92.     ODFrame* containingFrame = frame->AcquireContainingFrame(ev);
  93.     if ( !containingFrame )
  94.         containingFrame = otherFrame->AcquireContainingFrame(ev);
  95.  
  96. If references are released in one conditional branch, refbal warns it
  97. does not know whether the reference is released in all branches:
  98.     ODFrame* containingFrame = frame->AcquireContainingFrame(ev);
  99.     if ( condition )
  100.     {
  101.         // ... do something
  102.         ODReleaseObject(ev, containingFrame);
  103.     }
  104.     else
  105.     {
  106.         // ... do something else
  107.         ODReleaseObject(ev, containingFrame);
  108.     }
  109.  
  110. === Usage ==========================================================
  111. A typical command line to check file foo.c might look likes
  112.  
  113.     refbal -e foo.c
  114.  
  115. Invoking refbal with no argument will cause usage info to be printed:
  116.  
  117. ### Usage: refbal [option ...] filenames
  118.    options:
  119.      -a   # obsolete option (once affected assignment warnings)
  120.      -c   # console - if not -u, echo file progress to stderr
  121.      -e   # echo - print lines associated with errors
  122.      -f   # fields - include information about fFoo fields
  123.      -l   # local - suppress warnings about releasing values not acquired locally
  124.      -m   # members - include information about '_' members
  125.      -p   # path - suppress warnings about execution paths
  126.      -t   # temps - suppress warnings about ODTemp's
  127.      -u   # unbuffered - write to stderr (not stdout)
  128.    status:
  129.       0   # no error
  130.       1   # generic problem
  131.       2   # unknown option
  132.       3   # no filename specified
  133.       4   # cannot open file
  134.       9   # scope too deep
  135.      10   # generic file IO problem
  136.      11   # not enough memory
  137.      14   # internal error
  138.    version: Monday, July 10, 1995 1:15 PM
  139.  
  140. The -a option is obsolete.
  141.  
  142. Use the -c option if you are redirecting output from examining several files,
  143. and you want to have file progress indications output to stderr. E.g.:
  144.     refbal -c opendoc:ā‰ˆ.cā‰ˆ > opendoc.refbal
  145.     
  146. Use the -e option if you want to have lines printed which contain
  147. problems that refbal is reporting.  This option is generally recommended
  148. so you can hand-filter some of the warnings by examining the line which
  149. caused the warning.
  150.     
  151. Use the -f and -m options if you want to re-enable the generation of
  152. errors associated with variables that begin with either /_/ or /f[A-Z]/.
  153.  
  154. Use the -l and -p options to suppress warnings that might seem less
  155. useful, regarding release of variables not acquired locally (you 
  156. probably just got one from a collection), or regarding release of
  157. variables in a different scope than the original acquire (refbal
  158. cannot do flow analysis).
  159.  
  160. Use the -t option to suppress error messages related to temp variables
  161. (which have typenames beginning with either ODTemp or TempOD).  This is a
  162. good idea if you routinely swap values in and out of temp variables, or
  163. even release them because you know what you are doing.  Such usage may
  164. confuse refbal and it may generate many errors because it thinks
  165. temp variables should release themselves.
  166.  
  167. === Example ========================================================
  168. Suppose we run refbal on a file foo.c containing the following function. 
  169. (Note that it is just a random assemblage of stuff that will not compile.
  170. You should make sure code you scan at least compiles before you run refbal 
  171. on it; why confuse refbal more than necessary?)
  172.  
  173. ODFoo* MyClass:GetFoo( )
  174. {
  175.      ODFoo* returnedFoo = fetch->AcquireFoo( ); // returned, not released
  176.      ODFoo* droppedFoo = drop->AcquireFoo( );   // never released
  177.      TempODFoo tempFoo = temp->AcquireFoo( );   // auto-releases
  178.      
  179.      ODFoo* origFoo = orig->AcquireFoo( );   // released by ODCopyAndRelease
  180.      ODFoo* copiedFoo = ODCopyAndRelease(ev, origFoo);   // never released
  181.      
  182.     // presumably other member functions release the value acquired next:
  183.     _fIgnoredFoo = x->AcquireFoo( ); // SOM member variable
  184.         
  185.     unknown->Release(); // was never acquired locally
  186.         
  187.     random->AcquireFoo()->Random(); // did not assign result of AcquireFoo()
  188.  
  189.     ODBeer* frameBeer;
  190.     if ( flag->AcquireBool() ) // do not put Acquires in parentheses
  191.     {
  192.         frameBeer = _fFrame->AcquireMoreBeer(ev, kODNULL); // not released
  193.     }
  194.     else
  195.     {
  196.         frameBeer = _fFrame->AcquireBeer(ev, kODNULL); // released
  197.         ODFoo* copiedForm = xform->Copy(ev); // never released
  198.         ODFinalReleaseObject(ev, frameBeer);
  199.     }
  200.     ODReleaseObject(ev, tempFoo); // okay to release temp type this way
  201.     tempFoo->Release( ); // but not okay to release temp this way
  202.     
  203.     if (arbitrary)
  204.         return returnedFoo; // function GetFoo( ) does not contain "Acquire"
  205.     
  206.     ODReleaseObject(ev, returnedFoo); // releasing a returned value
  207.     
  208.     frameShape = RequestFrameShape();   // not released
  209.     borderShape = AdjustBorderShape();  // not released
  210.     baseMenuBar = CopyBaseMenuBar();    // not released
  211.  
  212.     myWindow = AcquireWindow();
  213.     myWindow = AcquireWindow(); // second assignment without a release
  214.     
  215.     myWindow->Close(); // releases myWindow
  216.     
  217.     myWindow->CloseAndRemove(); // already released by Close()
  218.     
  219.     ODStorageUnitView* suv = foobar->CreateView(); // not deleted
  220.     
  221.     return 0L;
  222. }
  223.  
  224. If you run refbal on foo.c with the -e option, you will get
  225. output that looks something like the following.  This example illustrates
  226. many of the kinds of apparent problems that refbal understands.
  227.  
  228. refbal -e foo.c
  229. %% foo.c ...
  230. ### warning: releasing unknown which was not locally acquired 
  231. File "foo.c"; Line 13
  232.     unknown->Release(); // was never acquired locally
  233. ### ERROR: must assign result of AcquireFoo() 
  234. File "foo.c"; Line 15
  235.     random->AcquireFoo()->Random(); // did not assign result of AcquireFoo()
  236. ### ERROR: cannot track Acquire "AcquireBool" inside parens 
  237. File "foo.c"; Line 18
  238.     if ( flag->AcquireBool() ) // do not put Acquires in parentheses
  239. ### warning: re-assigning frameBeer not released since previous assign 
  240. File "foo.c"; Line 24
  241.         frameBeer = _fFrame->AcquireBeer(ev, kODNULL); // released
  242. File "foo.c"; Line 20  # frameBeer assigned from AcquireMoreBeer()
  243.         frameBeer = _fFrame->AcquireMoreBeer(ev, kODNULL); // not released
  244. ### ERROR: copiedForm was not released
  245. File "foo.c"; Line 25  # copiedForm assigned from Copy()
  246.         ODFoo* copiedForm = xform->Copy(ev); // never released
  247. ### warning: releasing tempFoo (temp vars auto-release) 
  248. File "foo.c"; Line 29
  249.     tempFoo->Release( ); // but not okay to release temp this way
  250. File "foo.c"; Line 5  # tempFoo assigned from AcquireFoo()
  251.      TempODFoo tempFoo = temp->AcquireFoo( );   // auto-releases
  252. ### ERROR: returning acquired value returnedFoo from non-acquire function GetFoo 
  253. File "foo.c"; Line 32
  254.         return returnedFoo; // function GetFoo( ) does not contain "Acquire"
  255. ### warning: releasing returnedFoo returned earlier
  256. File "foo.c"; Line 34
  257.     ODReleaseObject(ev, returnedFoo); // releasing a returned value
  258. File "foo.c"; Line 3  # returnedFoo assigned from AcquireFoo()
  259.      ODFoo* returnedFoo = fetch->AcquireFoo( ); // returned, not released
  260. ### warning: re-assigning myWindow not released since previous assign 
  261. File "foo.c"; Line 41
  262.     myWindow = AcquireWindow(); // second assignment without a release
  263. File "foo.c"; Line 40  # myWindow assigned from AcquireWindow()
  264.     myWindow = AcquireWindow();
  265. ### ERROR: myWindow has already been released by Close()
  266. File "foo.c"; Line 45
  267.     myWindow->CloseAndRemove(); // already released by Close()
  268. File "foo.c"; Line 41  # myWindow assigned from AcquireWindow()
  269.     myWindow = AcquireWindow(); // second assignment without a release
  270. ### ERROR: baseMenuBar was not released
  271. File "foo.c"; Line 38  # baseMenuBar assigned from CopyBaseMenuBar()
  272.     baseMenuBar = CopyBaseMenuBar();    // not released
  273. ### ERROR: borderShape was not released
  274. File "foo.c"; Line 37  # borderShape assigned from AdjustBorderShape()
  275.     borderShape = AdjustBorderShape();  // not released
  276. ### ERROR: frameShape was not released
  277. File "foo.c"; Line 36  # frameShape assigned from RequestFrameShape()
  278.     frameShape = RequestFrameShape();   // not released
  279. ### ERROR: droppedFoo was not released
  280. File "foo.c"; Line 4  # droppedFoo assigned from AcquireFoo()
  281.      ODFoo* droppedFoo = drop->AcquireFoo( );   // never released
  282. ### ERROR: copiedFoo was not released
  283. File "foo.c"; Line 8  # copiedFoo assigned from ODCopyAndRelease()
  284.      ODFoo* copiedFoo = ODCopyAndRelease(ev, origFoo);   // never released
  285. ### ERROR: suv was not deleted
  286. File "foo.c"; Line 47  # suv assigned from CreateView()
  287.     ODStorageUnitView* suv = foobar->CreateView(); // not deleted
  288.  
  289.  
  290. === Syncing when lost ================================================
  291. What might confuse refbal?  What happens when refbal gets confused?  
  292. How does it sync up to continue parsing a file?
  293.  
  294. If refbal encounters unbalanced braces (which might not bother a compiler
  295. which understands ifdef's), then refbal will not know how to judge
  296. when variables go out of scope.  If refbal becomes confused, subsequent
  297. error messages may be inappropriate until it re-syncs.  
  298.  
  299. Refbal assumes that a left brace '{' occurring as the first character
  300. on a line is an indication of a function beginning, and will try to
  301. re-sync when it sees one.  (This seemed like a harmless assumption;
  302. please tell me all your functions place { on the beginning of new lines.)
  303.  
  304. Refbal tries to ignore the extra braces caused by extern "C":
  305.  
  306. #ifdef __cplusplus
  307. extern "C" {
  308. #endif
  309.  
  310. === Acquire Function Names ===========================================
  311.  
  312. Refbal might generate what seems like an excessive number of warnings.
  313. It tries to err on the side of too many, as opposed to too few, warnings.
  314. A likely cause of spurious warnings is the names of functions which
  315. look like an "acquire" function according to the heuristic rules that
  316. refbal applies.  If some of your function names fit certain rules,
  317. refbal will assume that you are returning an acquired ref-counted
  318. object and will warn that you should assign and subsequently release the
  319. returned value.  To make this situation less confusing, below is a
  320. description of exactly what qualifies as a refbal "acquire" function call.
  321.  
  322. Let id be the identifier used in a function call.  For example, in
  323.  
  324.     TempODStorageUnit su = draft->AcquireDraftProperties(ev);
  325.     
  326. id is "AcquireDraftProperties".  A series of tests is applied to id
  327. to determine whether it is an acquire, or not an acquire function.  Some
  328. of the tests are to exclude what otherwise would be taken as an acquire
  329. function.  The tests are ordered, when possible, to take advantage of
  330. quick rejections in order to speed up processing.
  331.  
  332. The expression "contains" means a simple substring test below, but
  333. "discretely contains" (a made-up term) means something slightly more
  334. specific: when id discretely contains "Acquire", this means that
  335. "Acquire" is a substring of id, and further that the next character
  336. is not an uppercase letter.  Thus FocusAcquired() does *not* discretely
  337. contain "Acquire" because of the trailing "d".  Thus, "discrete" means
  338. a discrete word in a function name.
  339.  
  340. Inclusions:
  341. id is considered an acquire function if
  342.     * id is exactly equal to one of the following
  343.         Acquire
  344.         AdjustBorderShape
  345.         ConstructRealPart
  346.         Copy
  347.         CopyBaseMenuBar
  348.         FindODWindow
  349.         ODAcquireObject
  350.         ODCopyAndRelease
  351.         RegisterWindow
  352.         RegisterWindowForFrame
  353.         RequestEmbeddedFrame
  354.         RequestFrameShape
  355.         RetrievePersistentObject
  356.     
  357.     * id discretely contains "Acquire"
  358.     
  359.     * id contains one of the following
  360.         AdjustBorderShape
  361.         RequestEmbeddedFrame
  362.         RequestFrameShape
  363.         
  364.     * id discretely contains a verb before a noun, where verbs are one of
  365.         Create       Make    New
  366.       and where nouns are one of
  367.         Container    Draft   Link         Part     Storage     Window
  368.         Document     Frame   MenuBar      Shape    Transform
  369.  
  370.     * the previous identifier is "new" and id is one of the following
  371.         CMDocument    ODBaseLinkSource    ODFileContainer ODPersistentObject
  372.         CMDraft       ODBaseShape         ODFrame         ODSemanticInterface
  373.         CMStorageUnit ODBaseTransform     ODLink          ODSettingsExtension
  374.         Clock         ODBentoContainer    ODLinkSource    ODShape
  375.         ClockSI       ODContainer         ODLinkSpec      ODShellPlugIn
  376.         
  377.         Container     ODDocument          ODMemContainer  ODStorageUnit
  378.         DragText      ODDraft             ODMenuBar       ODTransform
  379.         DrawSI        ODEditorSetup       ODPart          ODWindow
  380.         NoPart        ODEmbeddedContainer ODPartWrapper   ShellSI
  381.         ODBaseLink    ODExtension         ODPartsBin      SkeletonPart
  382.  
  383.         ODFacet       ODCanvas            ODStorageUnitView
  384.         
  385. Exclusions:
  386. id is *not* an acquire function if
  387.     * id is exactly equal to one of the following
  388.         NewCWindow    CreateRootFrame     CreateLinkSpec  CreateNewLink
  389.         CreateStandardPartToken
  390.     * id contains one of
  391.         Iter          Prox      Desc      Edit       parent_
  392.  
  393. === Parsing ==========================================================
  394.  
  395. Here is a more detailed specification of the tokens that refbal considers:
  396.  
  397.     * ; (the statement terminator)
  398.     * = (the assignment operator)
  399.     * parentheses
  400.     * braces
  401.     * left braces '{' which are the first character on a line
  402.     * identifiers
  403.     * identifiers containing one of the following substrings
  404.         Acquire  ODTemp  TempOD   Init
  405.         
  406.         AdjustBorderShape
  407.         RequestEmbeddedFrame
  408.         RequestFrameShape
  409.         
  410.         Create       Make    New
  411.         Container    Draft   Link         Part     Storage     Window
  412.         Document     Frame   MenuBar      Shape    Transform
  413.         
  414.         Iter         Prox    Desc         Edit       parent_
  415.         
  416.     * identifiers exactly equal to one of the following
  417.         new              delete                return     
  418.         Release          Acquire               Copy
  419.         ODReleaseObject  ODFinalReleaseObject  ODSafeReleaseObject   
  420.         ODDeleteObject   ODCopyAndRelease      ODAcquireObject
  421.                   
  422.         SOM_TRY          SOM_CATCH_ALL         SOM_ENDTRY
  423.         TRY              CATCH_ALL             ENDTRY
  424.         
  425.         Close            CloseAndRemove
  426.  
  427.         AdjustBorderShape
  428.         ConstructRealPart
  429.         CopyBaseMenuBar
  430.         FindODWindow
  431.         RegisterWindow
  432.         RegisterWindowForFrame
  433.         RequestEmbeddedFrame
  434.         RequestFrameShape
  435.         RetrievePersistentObject
  436.         
  437.         CreateView    CreateCanvas
  438.         
  439.         CMDocument    ODBaseLinkSource    ODFileContainer ODPersistentObject
  440.         CMDraft       ODBaseShape         ODFrame         ODSemanticInterface
  441.         CMStorageUnit ODBaseTransform     ODLink          ODSettingsExtension
  442.         Clock         ODBentoContainer    ODLinkSource    ODShape
  443.         ClockSI       ODContainer         ODLinkSpec      ODShellPlugIn
  444.         
  445.         Container     ODDocument          ODMemContainer  ODStorageUnit
  446.         DragText      ODDraft             ODMenuBar       ODTransform
  447.         DrawSI        ODEditorSetup       ODPart          ODWindow
  448.         NoPart        ODEmbeddedContainer ODPartWrapper   ShellSI
  449.         ODBaseLink    ODExtension         ODPartsBin      SkeletonPart
  450.  
  451.         ODFacet       ODCanvas            ODStorageUnitView
  452.         
  453.         RequestFrameShape  AdjustBorderShape  RequestEmbeddedFrame
  454.         
  455.         NewCWindow    CreateRootFrame     CreateLinkSpec  CreateNewLink
  456.         CreateStandardPartToken
  457.         
  458.     * identifiers which begin with "_"       (SOM "members")
  459.     * identifiers which begin with "f[A-Z]"  (C++ "fields")
  460.     
  461. Conversely, refbal carefully ignores the following:
  462.  
  463.     * identifiers exactly equal to one of the following
  464.         ev, if, else, for, while, case, switch
  465.     * The characters '*' and '&', which are removed with the white space.
  466.     * C and C++ comments both: /* */ and //
  467.     * the contents of strings enclosed by double quotes.
  468.  
  469. Given the information above, it becomes possible to view code through
  470. the eyes that refbal uses.  Consider the following piece of code:
  471.  
  472. SOM_Scope ODTransform*  SOMLINK ODFacetAcquireContentTransform(
  473.         ODFacet *somSelf, Environment *ev,
  474.         ODCanvas* biasCanvas)
  475. {
  476.     ODFacetData *somThis = ODFacetGetData(somSelf);
  477.     ODFacetMethodDebug("ODFacet","AcquireContentTransform");
  478.  
  479.     SOM_TRY
  480.         ODTransform* foo = kODNULL;
  481.     
  482.         if (!_fContentTransform)
  483.         {
  484.             foo = _fFrame->AcquireInternalTransform(ev, kODNULL);
  485.             _fContentTransform = foo->Copy(ev);
  486.             ODReleaseObject(ev, foo);
  487.             foo = somSelf->AcquireFrameTransform(ev, kODNULL);
  488.             _fContentTransform->PostCompose(ev, foo);
  489.             ODReleaseObject(ev, foo);
  490.         }
  491.         return BiasTransformGet(ev, _fContentTransform, biasCanvas);
  492.     SOM_CATCH_ALL
  493.     SOM_ENDTRY
  494.     return kODNULL;
  495. }
  496.  
  497. To refbal, the above code looks identical to the code below.  Note how
  498. much C and C++ information is left out. (Admittedly, some of it is left
  499. out because refbal doesn't think it is interesting when it looks at it.)
  500.  
  501. ODFacetAcquireContentTransform( )
  502. {
  503.     {
  504.         ODTransform foo = ;
  505.     
  506.         {
  507.             foo = AcquireInternalTransform ;
  508.             _fContentTransform = Copy ;
  509.             ODReleaseObject( foo );
  510.             foo = AcquireFrameTransform ;
  511.             ODReleaseObject( foo );
  512.         }
  513.     }
  514. }
  515.  
  516. <readme end of file>
  517.